import database as db
from twisted.python import log
import commands as cmd
from update_logic import locking, check_update
from collections import namedtuple

# request functions to perform an action
requests = {}

def req(func):
    requests[func.func_name.upper()] = func
    return func

def check_loc(player):
    if not player.loc_id:
        player.send(cmd.error("player hasn't joined the game(no loc_id)"))
        return False
    return True 

def check_creature(player):
    if not player.creature:
        player.send(cmd.error("player hasn't joined the game(no creature)"))
        return False
    return True 

def create_request(**kw):
    req = {'cost':20}
    req.update(kw)
    db.Location._requests[req['loc_id']].append(req)
    
########### SYSTEM REQUESTS: ###########    

def ENTER(player, loc_id, (x,y) = (None,None)):
    ''' Places character at designated location or if not specified: any free safe location '''
    
    if not player.creature:
        raise Exception, "No creature for player"+str(player)
    player.loc_id = loc_id
    @locking("system", id = loc_id)
    def add(lock):
        with db.Handler(True) as h:
            if x == None or y == None:
                targ_cell = None
            else:
                targ_cell = h.session.query(db.Cell)\
                .filter_by(loc_id = loc_id)\
                .filter_by(coords = (x,y)).one()
            create_request(loc_id = loc_id,
                           type = 'enter',
                           source = player.creature,
                           target_cell = targ_cell)
        player.committed = True
        check_update(player)
    
def EXIT(player):
    ''' removes creature from location '''
    # TODO:
 

########### REQUESTS: ###########    

@req
def MOVE(player, info):
    ''' Moves character in direction '''
    if not check_loc(player):
        return
    with db.Handler(True) as h:
        @locking("move", id = player.loc_id)
        def move(lock):
            cre = player.creature
            h.session.add(cre)
            if not cre.cell:
                player.send(cmd.error("You have not yet entered the game"))
                return
            
            if not info:
                player.send(cmd.error("No direction given for 'move'"))
                return
            
            if not set(info.lower()) <= set('nswe'):
                player.send(cmd.error("unknown direction given: "+info))
                return
            create_request(loc_id = player.loc_id,
                           type = "move",
                           source = player.creature,
                           info = info,
                           cost = 20)
    

@req
def COMMIT(player):
    if not check_loc(player):
        return
    @locking("commit", toThread=False, id=player.loc_id)
    def upd(lock):
        player.committed = True
        check_update(player)

@req
def CANCEL(player, n = None):
    ''' cancells n actions '''
    # TODO:

@req
def ATTACK(player, target):
    if not check_loc(player):
        return
    player.send(cmd.server_error("not yet implemented"))
    return
    @locking("attack", id = player.loc_id)
    def attack(lock):
        with db.Handler(True) as h:
            cre = player.creature
            h.session.add(cre)
            if not cre.cell:
                player.send(cmd.error("You have not yet entered the game"))
                return  
            target_c = cre.location.strid_objects[target]      
            # TODO: request
            
@req
def PICK(player, target):
    item = int(target)
    # TODO: check if target in same location as player and is visible
    if not (check_loc(player) and
            check_creature(player)):
        return
    @locking("pick", id = player.loc_id)
    def pick(lock):
        with db.Handler(True) as h:
            l = h.get_location(player.loc_id)  
            player.creature = h.merge(player.creature)
            items = dict(l.id_items)
            items.update({i.id:i for i in player.creature.inventory}) 
              
            if item not in items:
                player.send(cmd.error("No item with ID:"+str(item)))
                return
            create_request(loc_id = player.loc_id,
                           type = "pick",
                           source = player.creature,
                           target = items[item],
                           cost = 12)


@req 
def DROP(player, target):
    item = int(target)
    # TODO: check if target in same location as player and is visible
    if not (check_loc(player) and
            check_creature(player)):
        return

    @locking("drop", id = player.loc_id)
    def drop(lock):
        with db.Handler() as h:
            h.add(player.creature)
            l = h.get_location(player.loc_id) 
            items = dict(l.id_items)
            items.update({i.id:i for i in player.creature.inventory})
            if item not in items:
                player.send(cmd.error("No item with ID:"+str(item)))
                return
            create_request(loc_id = player.loc_id,
                           type = "drop",
                           source = player.creature,
                           target = items[item],
                           cost = 10)
                           
                           
